package com.abel.bigwater.data.kente

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.data.mapper.DataMapper
import com.abel.bigwater.data.mapper.RtuMapper
import com.abel.bigwater.model.BwData
import com.abel.bigwater.model.BwRtu
import com.abel.bigwater.model.BwRtuLog
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import kotlinx.coroutines.async
import org.slf4j.LoggerFactory
import java.util.*

open class KenteTcpHandler(val dataMapper: DataMapper, val rtuMapper: RtuMapper, val firmId: String, val firmCode: String) : ChannelInboundHandlerAdapter() {
    /**
     * Calls [ChannelHandlerContext.fireChannelRead] to forward
     * to the next [ChannelInboundHandler] in the [ChannelPipeline].
     *
     * Sub-classes may override this method to change behavior.
     */
    override fun channelRead(ctx: ChannelHandlerContext?, msg: Any?) {
        lgr.info("GOT TCP: ${msg}")

        if (msg is KenteWaitComm) {
            val extId = "${firmCode}${msg.rtuId}"
            storeRtuLog(extId, msg)
            lgr.info("rtu-log入库: ${extId}")
        } else if (msg is KentePacket) {
            try {
                storeData(msg)
            } catch (ex: Exception) {
                lgr.error("保存数据异常: ${ex.message}", ex)
            }
        }

        ctx!!.writeAndFlush(
                ctx.alloc().buffer().apply {
                    writeBytes("OK".toByteArray())
                })
        lgr.info("Write Back: OK")
    }

    protected fun storeData(msg: KentePacket) {
        val extId = "${firmCode}${msg.rtuId}"

        kotlin.run {
            val data = BwData().also {
                it.meterId = extId
                it.extId = extId
                it.firmId = firmId

                it.sampleTime = msg.sampleTime
                it.literPulse = 1000
                it.forwardDigits = msg.forwardReading
                it.revertDigits = msg.revertReading
                it.avgFlow = msg.flow?.toDouble()

                it.pressure = msg.pressure?.times(100.0)
                it.pressureDigits = msg.pressure?.times(100.0)
            }

            if (1 > dataMapper.updateRealtime(data)) dataMapper.insertRealtime(data)
        }

        kotlin.run {
            val rtu = BwRtu().also {
                it.rtuId = extId
                it.meterId = extId
                it.firmId = firmId

                it.literPulse = 1000
                it.forwardReading = msg.forwardReading
                it.revertReading = msg.revertReading

                it.voltWarn = msg.rtuVolt ?: 0
                it.rssi = msg.rssi ?: 0
                it.cutWarn = msg.commError?.toShort() ?: 0
                it.maxWarn = msg.flowMaxError?.toShort() ?: 0

                it.lastDataTime = msg.sampleTime
                it.lastCmd = KentePacket.head
                it.lastTime = msg.sampleTime
                it.lastText = msg.msg!!.take(200)
            }

            if (1 > rtuMapper.updateRtu(rtu)) {
                rtuMapper.addRtu(rtu.apply {
                    this.firstTime = Date()
                    this.firstCmd = KentePacket.head
                    this.firstText = msg.msg!!.take(40)
                })
            }
        }

        kotlin.run {
            storeRtuLog(extId, msg)
        }

        lgr.info("Kente消息入库: ${extId} - ${msg.sampleTime}: ${msg.forwardReading}")
    }

    /**
     * 日志.
     */
    private fun storeRtuLog(extId: String, msg: KentePacket): Int {
        val rtuLog = BwRtuLog().also {
            it.rtuId = extId
            it.meterId = extId
            it.firmId = firmId

            it.literPulse = 1000
            it.forwardReading = msg.forwardReading
            it.revertReading = msg.revertReading

            it.voltWarn = msg.rtuVolt ?: 0
            it.rssi = msg.rssi ?: 0
            it.cutWarn = msg.commError?.toShort() ?: 0
            it.maxWarn = msg.flowMaxError?.toShort() ?: 0

            it.logTime = Date()
            it.logCmd = msg.command
            it.logText = msg.msg!!.take(200)
            it.logLen = msg.len ?: 0
        }

        return rtuMapper.addRtuLog(rtuLog)
    }

    /**
     * Calls [ChannelHandlerContext.fireExceptionCaught] to forward
     * to the next [ChannelHandler] in the [ChannelPipeline].
     *
     * Sub-classes may override this method to change behavior.
     */
    override fun exceptionCaught(ctx: ChannelHandlerContext?, cause: Throwable?) {
        lgr.error("错误退出:${ctx?.channel()?.remoteAddress()} - ${cause?.message}", cause)

        ctx?.close()
    }

    companion object {
        private val lgr = LoggerFactory.getLogger(KenteTcpHandler::class.java)
    }
}